${escapeHTML(title)}
${escapeHTML(description)}
${ date ? `"use strict"; /* ========================================================= HEXORA SEARCH — MAIN.JS ========================================================= */ const CONFIG = { searchEndpoint: "/api/search", newsEndpoint: "/api/news", map: { defaultCenter: [91.7362, 26.1445], defaultZoom: 5, osmTiles: "https://tile.openstreetmap.org/{z}/{x}/{y}.png", satelliteTiles: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", geocoder: "https://nominatim.openstreetmap.org/search" } }; let map = null; let mapMarker = null; /* ========================================================= HELPERS ========================================================= */ function $(selector) { return document.querySelector(selector); } function $$(selector) { return Array.from(document.querySelectorAll(selector)); } function escapeHTML(value) { return String(value ?? "") .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function safeURL(url) { try { const value = String(url || "").trim(); if (!value) { return "#"; } if ( value.startsWith("http://") || value.startsWith("https://") ) { return value; } return new URL(value, window.location.origin).href; } catch { return "#"; } } function formatDate(value) { if (!value) return ""; const date = new Date(value); if (Number.isNaN(date.getTime())) { return String(value); } return date.toLocaleDateString("en-IN", { day: "numeric", month: "short", year: "numeric" }); } /* ========================================================= MAPLIBRE ========================================================= */ function loadMapLibre() { return new Promise((resolve, reject) => { if (window.maplibregl) { resolve(window.maplibregl); return; } const oldScript = document.querySelector( 'script[data-hexora-maplibre="true"]' ); if (oldScript) { oldScript.addEventListener("load", () => { resolve(window.maplibregl); }); oldScript.addEventListener("error", reject); return; } const script = document.createElement("script"); script.src = "https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.js"; script.async = true; script.dataset.hexoraMaplibre = "true"; script.onload = () => { if (window.maplibregl) { resolve(window.maplibregl); } else { reject(new Error("MapLibre failed to load.")); } }; script.onerror = () => { reject(new Error("Could not load MapLibre.")); }; document.head.appendChild(script); }); } /* ========================================================= MAP STYLES ========================================================= */ function streetStyle() { return { version: 8, sources: { hexoraOSM: { type: "raster", tiles: [CONFIG.map.osmTiles], tileSize: 256, attribution: "© OpenStreetMap contributors" } }, layers: [ { id: "hexora-osm", type: "raster", source: "hexoraOSM" } ] }; } function satelliteStyle() { return { version: 8, sources: { hexoraSatellite: { type: "raster", tiles: [CONFIG.map.satelliteTiles], tileSize: 256, attribution: "© Esri" } }, layers: [ { id: "hexora-satellite", type: "raster", source: "hexoraSatellite" } ] }; } /* ========================================================= INITIALIZE MAP ========================================================= */ async function initializeMap() { const container = $("#hexoraMap"); if (!container) { return null; } if (map) { return map; } try { const maplibregl = await loadMapLibre(); map = new maplibregl.Map({ container: "hexoraMap", style: streetStyle(), center: CONFIG.map.defaultCenter, zoom: CONFIG.map.defaultZoom, pitch: 0, bearing: 0, attributionControl: true }); map.addControl( new maplibregl.NavigationControl({ visualizePitch: true }), "top-right" ); map.on("error", (event) => { console.error("HEXORA Map error:", event); }); return map; } catch (error) { console.error("HEXORA Map loading error:", error); const info = $("#mapInfo"); if (info) { info.textContent = "HEXORA Map could not be loaded."; info.style.display = "block"; } return null; } } /* ========================================================= OPEN MAP ========================================================= */ async function openMap(options = {}) { const mapView = $("#mapView"); const searchView = $("#searchView"); if (!mapView) { return; } if (searchView) { searchView.classList.remove("active"); } const home = $("#homeView"); if (home) { home.style.display = "none"; } mapView.classList.add("active"); const instance = await initializeMap(); if (!instance) { return; } setTimeout(() => { instance.resize(); if (options.center) { instance.flyTo({ center: options.center, zoom: options.zoom || 13, speed: 1.2, essential: true }); } }, 100); } /* ========================================================= CLOSE MAP ========================================================= */ function closeMap() { const mapView = $("#mapView"); if (mapView) { mapView.classList.remove("active"); } } /* ========================================================= MAP STYLE CHANGE ========================================================= */ function changeMapStyle(type) { if (!map) { return; } const center = map.getCenter().toArray(); const zoom = map.getZoom(); const pitch = map.getPitch(); const bearing = map.getBearing(); if (type === "satellite") { map.setStyle(satelliteStyle()); } else { map.setStyle(streetStyle()); } map.once("style.load", () => { map.jumpTo({ center, zoom, pitch, bearing }); }); } /* ========================================================= MAP SEARCH ========================================================= */ async function searchMapPlace(query) { const value = String(query || "").trim(); if (!value) { return; } const info = $("#mapInfo"); if (info) { info.textContent = "Searching..."; info.style.display = "block"; } try { const url = new URL(CONFIG.map.geocoder); url.searchParams.set("q", value); url.searchParams.set("format", "json"); url.searchParams.set("limit", "5"); url.searchParams.set("addressdetails", "1"); const response = await fetch(url.toString(), { headers: { Accept: "application/json" } }); if (!response.ok) { throw new Error("Map search failed."); } const places = await response.json(); if (!Array.isArray(places) || places.length === 0) { if (info) { info.textContent = "Place not found."; } return; } const place = places[0]; const lat = Number(place.lat); const lon = Number(place.lon); if (!Number.isFinite(lat) || !Number.isFinite(lon)) { throw new Error("Invalid coordinates."); } await openMap({ center: [lon, lat], zoom: 13 }); if (!map) { return; } map.flyTo({ center: [lon, lat], zoom: 13, speed: 1.2, essential: true }); addMapMarker([lon, lat]); if (info) { info.textContent = place.display_name || value; } } catch (error) { console.error("HEXORA map search error:", error); if (info) { info.textContent = "Map search failed. Please try again."; } } } /* ========================================================= MAP MARKER ========================================================= */ function addMapMarker(coordinates) { if (!map || !window.maplibregl) { return; } if (mapMarker) { mapMarker.remove(); } mapMarker = new window.maplibregl.Marker() .setLngLat(coordinates) .addTo(map); } /* ========================================================= LOCATION ========================================================= */ function locateUser() { if (!navigator.geolocation) { alert( "Your browser does not support location." ); return; } const info = $("#mapInfo"); if (info) { info.textContent = "Getting your location..."; info.style.display = "block"; } navigator.geolocation.getCurrentPosition( async (position) => { const latitude = position.coords.latitude; const longitude = position.coords.longitude; await openMap({ center: [longitude, latitude], zoom: 15 }); if (!map) { return; } map.flyTo({ center: [longitude, latitude], zoom: 15, speed: 1.2, essential: true }); addMapMarker([ longitude, latitude ]); if (info) { info.textContent = "Your current location"; } }, (error) => { console.error( "HEXORA location error:", error ); if (info) { info.textContent = "Location permission denied or unavailable."; } }, { enableHighAccuracy: true, timeout: 15000, maximumAge: 0 } ); } /* ========================================================= RESET MAP ========================================================= */ function resetMap() { if (!map) { return; } if (mapMarker) { mapMarker.remove(); mapMarker = null; } map.flyTo({ center: CONFIG.map.defaultCenter, zoom: CONFIG.map.defaultZoom, pitch: 0, bearing: 0, speed: 1.2, essential: true }); const info = $("#mapInfo"); if (info) { info.textContent = ""; info.style.display = "none"; } } /* ========================================================= 3D VIEW ========================================================= */ function enable3D() { if (!map) { return; } map.easeTo({ pitch: 60, bearing: -20, duration: 1000 }); } /* ========================================================= FULLSCREEN ========================================================= */ function fullscreenMap() { const mapView = $("#mapView"); if (!mapView) { return; } if (!document.fullscreenElement) { if (mapView.requestFullscreen) { mapView.requestFullscreen(); } } else { document.exitFullscreen(); } setTimeout(() => { if (map) { map.resize(); } }, 500); } /* ========================================================= SEARCH ========================================================= */ async function performSearch(query) { const value = String(query || "").trim(); if (!value) { return; } closeMap(); const home = $("#homeView"); const searchView = $("#searchView"); if (home) { home.style.display = "none"; } if (searchView) { searchView.classList.add("active"); } const resultMeta = $("#resultMeta"); const results = $("#results"); if (resultMeta) { resultMeta.textContent = `Searching for "${value}"...`; } if (results) { results.innerHTML = `
Search service is temporarily unavailable. Please try again.
Try another search query.
${escapeHTML(description)}
${ date ? `${escapeHTML( description )}
` : "" } ${ date ? ` ${escapeHTML( formatDate(date) )} ` : "" }